package com.example.androidtest.demo3;

import android.util.Log;

import androidx.annotation.Nullable;

import org.jetbrains.annotations.NotNull;

import io.netty.channel.socket.SocketChannel;
import java.util.concurrent.TimeUnit;

import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoop;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.timeout.IdleStateHandler;
import io.netty.util.CharsetUtil;
import io.netty.util.HashedWheelTimer;

/**
 * Netty安卓客户端
 */
public class NettyClient {

    private int reconnectCount = 0;

    //端口号
    private int port = 27410;
    //主机地址
    private String host = "120.25.123.172";


    private SocketChannel socketChannel = null;//连接通道
    private ChannelFuture future = null;//
    private EventLoopGroup eventLoopGroup;//线程池
    private Bootstrap bootstrap;
    private HashedWheelTimer timer;
    private ConnectorIdleStateTrigger idleStateTrigger;

    public static NettyClient client;

    public synchronized static NettyClient getInstance() {
        if (null == client) {
            client = new NettyClient();
        }
        return client;
    }


    //设置主机地址
    public void setHost(String host,int port) {
        this.host = host;
        this.port = port;
    }


    //建立连接（建议在Thread或AsyncTask中进行连接）
    public synchronized void connect(final ConnectedCallback onConnectedCallback) {
//        Log.i("nettyConnect", "connect");
//        Log.i("nettyConnect", "host == " + host);


        timer = new HashedWheelTimer();
        idleStateTrigger = new ConnectorIdleStateTrigger();

        bootstrap = new Bootstrap();

        //初始化线程组
        eventLoopGroup = new NioEventLoopGroup();

        //配置启动参数
        bootstrap.group(eventLoopGroup)
                .channel(NioSocketChannel.class)
                .handler(new LoggingHandler(LogLevel.INFO));

        final ConnectionWatchdog watchDog = new ConnectionWatchdog(bootstrap, timer, port, host, true) {
            @Override
            //重新连接成功的回调
            public void reconnectSuccess(SocketChannel channel) {
                if( channel != null) {
                    socketChannel = channel;
                } else {
//                    Log.i("netty", "重连后 SocketChannel == null");
                }
            }
            @NotNull
            @Override
            public ChannelHandler[] handlers() {
                ChannelHandler[] handlers = {
                        this,
                        new IdleStateHandler(30, 5, 0, TimeUnit.SECONDS),
                        idleStateTrigger,//空闲处理
                        new NettyClientHandler()//接收数据处理器
                };
                return handlers;
            }
        };

        try {
            synchronized(bootstrap) {
                bootstrap.handler(new ChannelInitializer<Channel>() {
                    @Override
                    protected void initChannel(Channel channel) throws Exception {
                        channel.pipeline()
                                .addLast(watchDog.handlers());//处理数据接收
                    }
                });


                /**
                 1.异步连接到远程对等节点。调用立即返回并提供 ChannelFuture
                 2.操作完成后通知注册一个 ChannelFutureListener
                 3.当 operationComplete() 调用时检查操作的状态
                 4.如果成功就创建一个 ByteBuf 来保存数据。
                 5.异步发送数据到远程。再次返回ChannelFuture
                 6.如果有一个错误则抛出 Throwable,描述错误原因
                 */
                //开始建立连接
                future = bootstrap.connect(host, port);
                //连接的监听
                future.addListener(new ChannelFutureListener() {
                    @Override
                    public void operationComplete(ChannelFuture channelFuture) throws Exception {

                        //连接成功
                        if(future.isSuccess()) {

//                            Log.d("netty", "connect success !");

                            socketChannel = (SocketChannel) future.channel();
                            Thread.sleep(200);

                            if (onConnectedCallback != null) {
                                onConnectedCallback.onConnected();
                            }

                        } else {//连接失败（重连）

//                            Log.d("netty", "connect failed !");

                            EventLoop loop = ((ChannelFuture)future).channel().eventLoop();

                            loop.schedule(new Runnable() {
                                @Override
                                public void run() {

//                                    Log.i("netty", "服务端链接不上，开始重连操作...");
                                    connect(onConnectedCallback);
                                }
                            }, 5L, TimeUnit.SECONDS);

                        }
                    }
                });
            }
        } catch (Exception e) {

        }
    }


    //心跳
    public void ping() {
        try {
//            Log.i("netty ping", "ping");
            ByteBuf bb = Unpooled.wrappedBuffer("心跳内容".getBytes());
            if (socketChannel != null)
                socketChannel.writeAndFlush(bb);
        } catch (Exception e) {
//            Log.i("netty", e.toString());
        }
    }


    //发送消息
    public void send(String str) {

        if (socketChannel != null) {

//            Log.i("nettySend", str);

            //需要发送的内容
            ByteBuf bb = Unpooled.wrappedBuffer((str.getBytes(CharsetUtil.UTF_8)));

            //发送消息
            socketChannel.writeAndFlush(bb)//发送消息
                    .addListener(new ChannelFutureListener() {//发送消息的监听

                        public void operationComplete(ChannelFuture future) throws Exception {

                            if(future.isSuccess()) {//发送成功
//                                Log.i("nettySend", "send success");
                            } else {//发送失败
//                                Log.i("nettySend", "send fail,reason is {}" + future.cause().getMessage());
                            }
                        }
                    });

            //通过已经关闭
        } else {
//            Log.i("nettySend", "socketChannel == null");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //重新连接
            NettyClient.getInstance().connect(new ConnectedCallback() {
                @Override
                public void onConnected() {

                }
            });
        }
    }


    //判断是否连接
    public boolean isConnect() {
        return socketChannel != null;
    }

    //断开连接
    public void disconnect(String where) {
//        Log.i("nettyDisconnect", "disconnect " + where);
        try {
            if (socketChannel != null) {
                socketChannel.close();
                socketChannel = null;
            }
            if (eventLoopGroup != null) {
                eventLoopGroup.shutdownGracefully();
                eventLoopGroup = null;
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    //连接成功的回调接口
    public interface ConnectedCallback {
        void onConnected();
    }
}

